--# Main
-- 2.5D Platformer
-- author: @Dwins
-- from: https://codea.io/talk/discussion/7543/2-5d-platformer

supportedOrientations(LANDSCAPE_ANY)
displayMode(FULLSCREEN)
function setup()
    map = {}
    map[1] = {
    "OXXOOOOOOOOOOOOOOOOOOOXOOOOOOO",
    "OOOXXXOOXXOOOOXXOOOOOOOOOXOOOO",
    "OXXXXXXXXXXXXXXXXXXXOOOOOOOOOO",
    "OXXXXP             XOOOOOXOOOO",
    "OXXXXXXXXXX|   XX  XXXXXXXOOOO",
    "XOOXXXOOOOXX|            XOOOO",
    "OOOXXXXXOOXXXXXXXXXXXXX| XOOOO",
    "OOXXXXXXXXXOOOXXOXOOOXXX XXOOO",
    "OOXG     |XXOOOXOOOOOOOX XOOOO",
    "OOXX XXX  |XXXOOOXOOOOOX XXOOO",
    "OOOX       |XXXXXXXXXXX/ |XOOO",
    "OOOOXXXXX                  XOO",
    "OOOOOOOXXXXXXXXXXXXXXXXXXXXXOO",
    "OOOOOOOOXOOOOOXXOOOOOOOOXOOOOO",
    "OOOXOOOOOOOOOOOOOXOOOOOOOOXOOO"
    }
    map[2] = {
    "OXXOOOOOOOOOOOOOOOOOOOXOOOOOOO",
    "OOOXXXOOXXOOOOXXOOOOOOOOOXOOOO",
    "OXXXXXXXXXXXXXXXXXXXOOOOOOOOOO",
    "OXXXXP  |XOOOOOXOOOOOOOOOOOOOO",
    "OXXXXXX| |XXXXXXXXXOOOOOOOOOOO",
    "XOOXXXOX| |XXOOOO        XOOOO",
    "OOOXXXXXX| |XOOOOOOOOOOOOOOOOO",
    "OOXXXXXXXX| |XXOOOOOOOOOOOOOOO",
    "OX       |X| |XOOOOOOOOOOOOOOO",
    "OX XX|    |X| |XXOOOOOXXXXXXOO",
    "OX XXXX|   |X| |XXXXXXX    XOO",
    "OX XXXXXX|                 XOO",
    "OX XXXXXXXXXXXXXXXX|     XXXOO",
    "XX                       XXOOO",
    "XXXXXXXXXXXXXXXXXXXXXXXXXXXOOO",
    "OOOXOOOOOOOOOOOOOXOOOOOOOOXOOO"
    }
    level = 1
    physicsScale = 50
    stage = mesh()
    colorTab = {f=color(200),b=color(180),
                u=color(255),d=color(150),l=color(170),r=color(160),
                ul=color(190),ur=color(160),dl=color(150),dr=color(140)}
    
    createStage(level)
    camPos = vec2(player.x,player.y)/physicsScale
    camPos = vec2(20,0)
    
    createSphere()
    spin = 0
    player.gravityScale = 3
end

function draw()
    perspective(30)
    power = math.min(math.max(-25,Gravity.x*50),25)
    player:applyForce(vec2(power,0))
    camTarget = player.position/physicsScale-player.linearVelocity/50+vec2(0,1)
    camPos.x = camPos.x + (camTarget.x-camPos.x)/20
    camPos.y = camPos.y + (camTarget.y-camPos.y)/40
    camera(camPos.x,camPos.y,10,player.x/physicsScale,player.y/physicsScale,1/2,-Gravity.x,2,0)
    background(colorTab.f)
    stage:draw()
    translate(player.x/physicsScale,player.y/physicsScale,1/2)
    rotate(30,1,1,1)
    rotate(player.angle,-0.1,0,0.1)
    sphere:draw()
    
end

function touched(touch)
    if touch.state == BEGAN then player.gravityScale = -player.gravityScale end
end

function createSphere()
    sphere = mesh()
    local verts = {}
    local colors = {}
    
    local res = 9
    local s = 3/8
    local long,lat = 360/res,180/res
    for la = 0,lat-1 do
        for lo = 1,long do
            local c1 = color(0,50-la*lat/5,255-la*lat/5)
            local c2 = color(0,50-la*lat/5,255-(la+1)*lat/5)
            
            local x,y,z
            x = math.cos(math.rad(lo*res))*math.cos(math.rad(90-la*res))*s
            y = math.sin(math.rad(90-la*res))*s
            z = math.sin(math.rad(lo*res))*math.cos(math.rad(90-la*res))*s
            local v1 = vec3(x,y,z)
            x = math.cos(math.rad((lo+1)*res))*math.cos(math.rad(90-la*res))*s
            y = math.sin(math.rad(90-la*res))*s
            z = math.sin(math.rad((lo+1)*res))*math.cos(math.rad(90-la*res))*s
            local v2 = vec3(x,y,z)
            x = math.cos(math.rad((lo+1)*res))*math.cos(math.rad(90-(la+1)*res))*s
            y = math.sin(math.rad(90-(la+1)*res))*s
            z = math.sin(math.rad((lo+1)*res))*math.cos(math.rad(90-(la+1)*res))*s
            local v3 = vec3(x,y,z)
            x = math.cos(math.rad(lo*res))*math.cos(math.rad(90-(la+1)*res))*s
            y = math.sin(math.rad(90-(la+1)*res))*s
            z = math.sin(math.rad(lo*res))*math.cos(math.rad(90-(la+1)*res))*s
            local v4 = vec3(x,y,z)
            local v = {v1,v2,v3,v3,v4,v1}
            local c = {c1,c1,c2,c2,c2,c1}
            for i,vv in pairs(v) do table.insert(verts,vv) end
            for i,cc in pairs(c) do table.insert(colors,cc) end
        end
    end
    sphere.vertices = verts
    sphere.colors = colors
end


--# CreateStage
function createStage(lev)
    currentMap = map[lev]
    local cm = currentMap
    local verts = {}
    local colors = {}
    local height = #cm + 1
    for x = 1,#cm[1] do
        for y = 1,#cm do
            local char = string.sub(cm[height-y],x,x)
            local addVerts,addCols
            if char == " " then addVerts,addCols = createPath(x,y) end
            if char == "X" then addVerts,addCols = createRandomWall(x,y) end
            if char == "O" then addVerts,addCols = createNormalWall(x,y) end
            if char == "/" then addVerts,addCols = createSlant(x,y,char,height,cm) end
            if char == "|" then addVerts,addCols = createSlant(x,y,char,height,cm) end
            if char == "P" then addVerts,addCols = createPlayer(x,y) end
            if char == "G" then addVerts,addCols = createGoal(x,y) end
            for i,v in pairs(addVerts) do table.insert(verts,v) end
            for i,c in pairs(addCols) do table.insert(colors,c) end
        end
    end
    createBorders(cm,verts,colors)
    print(#verts,#colors)
    stage.vertices = verts
    stage.colors = colors
    
    createWalls(cm)
end

function createPath(xx,yy)
    local x,y = xx-1,yy-1
    local v1 = vec3(x,yy,0)
    local v2 = vec3(xx,yy,0)
    local v3 = vec3(xx,y,0)
    local v4 = vec3(x,y,0)
    local cb = colorTab.b
    local v = {v1,v2,v3,v3,v4,v1}
    local c = {cb,cb,cb,cb,cb,cb}
    return v,c
end

function createRandomWall(xx,yy)
    local n = math.random(1,5)
    local d = 1+n/5
    local x,y = xx-1,yy-1
    local v1 = vec3(x,yy,d)
    local v2 = vec3(xx,yy,d)
    local v3 = vec3(xx,y,d)
    local v4 = vec3(x,y,d)
    local v5 = vec3(x,yy,0)
    local v6 = vec3(xx,yy,0)
    local v7 = vec3(xx,y,0)
    local v8 = vec3(x,y,0)
    local cf = color(200+n*10)
    local cu,cd,cl,cr = colorTab.u,colorTab.d,colorTab.l,colorTab.r
    local v = {v1,v2,v3,v3,v4,v1,
               v1,v2,v6,v6,v5,v1,
               v4,v3,v7,v7,v8,v4,
               v1,v5,v8,v8,v4,v1,
               v2,v6,v7,v7,v3,v2}
    local c = {cf,cf,cf,cf,cf,cf,
               cu,cu,cu,cu,cu,cu,
               cd,cd,cd,cd,cd,cd,
               cl,cl,cl,cl,cl,cl,
               cr,cr,cr,cr,cr,cr}
    return v,c
end

function createNormalWall(xx,yy)
    local x,y = xx-1,yy-1
    local v1 = vec3(x,yy,1)
    local v2 = vec3(xx,yy,1)
    local v3 = vec3(xx,y,1)
    local v4 = vec3(x,y,1)
    local cf = colorTab.f
    local v = {v1,v2,v3,v3,v4,v1}
    local c = {cf,cf,cf,cf,cf,cf}
    return v,c
end

function createSlant(xx,yy,char,height,cm)
    local n = math.random(1,5)
    local d = 1+n/5
    local x,y = xx-1,yy-1
    local v1 = vec3(x,yy,d)
    local v2 = vec3(xx,yy,d)
    local v3 = vec3(xx,y,d)
    local v4 = vec3(x,y,d)
    local v5 = vec3(x,yy,0)
    local v6 = vec3(xx,yy,0)
    local v7 = vec3(xx,y,0)
    local v8 = vec3(x,y,0)
    local cf,cb = color(200+n*10),colorTab.b
    local cu,cd,cl,cr = colorTab.u,colorTab.d,colorTab.l,colorTab.r
    local cul,cur,cdl,cdr = colorTab.ul,colorTab.ur,colorTab.dl,colorTab.dr
    local v,c = {},{}
    local up = string.sub(cm[height-yy-1],xx,xx)
    local down = string.sub(cm[height-yy+1],xx,xx)
    local left = string.sub(cm[height-yy],xx-1,xx-1)
    local right = string.sub(cm[height-yy],xx+1,xx+1)
    if char == "/" then
        if up == "X" or left == "X" then
            v = {v2,v1,v4,v8,v7,v6,
                 v1,v2,v6,v6,v5,v1,
                 v1,v4,v8,v8,v5,v1,
                 v2,v6,v8,v8,v4,v2}
            c = {cf,cf,cf,cb,cb,cb,
                 cu,cu,cu,cu,cu,cu,
                 cl,cl,cl,cl,cl,cl,
                 cdr,cdr,cdr,cdr,cdr,cdr}
        elseif down == "X" or right == "X" then
            v = {v2,v3,v4,v8,v5,v6,
                 v4,v3,v7,v7,v8,v4,
                 v2,v6,v7,v7,v3,v2,
                 v2,v4,v8,v8,v6,v2}
            c = {cf,cf,cf,cb,cb,cb,
                 cd,cd,cd,cd,cd,cd,
                 cr,cr,cr,cr,cr,cr,
                 cul,cul,cul,cul,cul,cul}
        end
    elseif char == "|" then
        if up == "X" or right == "X" then
            v = {v1,v2,v3,v7,v8,v5,
                 v1,v2,v6,v6,v5,v1,
                 v2,v6,v7,v7,v3,v2,
                 v1,v3,v7,v7,v5,v1}
            c = {cf,cf,cf,cb,cb,cb,
                 cu,cu,cu,cu,cu,cu,
                 cr,cr,cr,cr,cr,cr,
                 cdl,cdl,cdl,cdl,cdl,cdl}
        elseif down == "X" or left == "X" then
            v = {v3,v4,v1,v5,v6,v7,
                 v3,v4,v8,v8,v7,v3,
                 v1,v4,v8,v8,v5,v1,
                 v1,v3,v7,v7,v5,v1}
            c = {cf,cf,cf,cb,cb,cb,
                 cd,cd,cd,cd,cd,cd,
                 cl,cl,cl,cl,cl,cl,
                 cur,cur,cur,cur,cur,cur}
        end
    end
    return v,c
end

function createPlayer(x,y)
    local v,c = createPath(x,y)
    player = physics.body(CIRCLE,3/8*physicsScale)
    player.position = vec2(x-1/2,y-1/2)*physicsScale
    player.restitution = 0
    player.friction = 1
    return v,c
end

function createGoal(x,y)
    local v,c = createPath(x,y)
    return v,c
end

function createBorders(cm,verts,colors)
    local w,h = #cm[1],#cm
    local ww,hh = w+1,h+1
    local cf = colorTab.f
    local v = {vec3(0,0,1),vec3(w,0,1),vec3(w,-1,1),
               vec3(w,-1,1),vec3(0,-1,1),vec3(0,0,1),
               vec3(w,-1,1),vec3(ww,-1,1),vec3(ww,hh,1),
               vec3(ww,hh,1),vec3(w,hh,1),vec3(w,-1,1),
               vec3(0,hh,1),vec3(w,hh,1),vec3(w,h,1),
               vec3(w,h,1),vec3(0,h,1),vec3(0,hh,1),
               vec3(0,-1,1),vec3(-1,-1,1),vec3(-1,hh,1),
               vec3(-1,hh,1),vec3(0,hh,1),vec3(0,-1,1),
               vec3(-1,-1,1),vec3(ww,-1,1),vec3(ww,-1,0),
               vec3(ww,-1,0),vec3(-1,-1,0),vec3(-1,-1,1),
               vec3(ww,-1,1),vec3(ww,hh,1),vec3(ww,hh,0),
               vec3(ww,hh,0),vec3(ww,-1,0),vec3(ww,-1,1),
               vec3(ww,hh,1),vec3(-1,hh,1),vec3(-1,hh,0),
               vec3(-1,hh,0),vec3(ww,hh,0),vec3(ww,hh,1),
               vec3(-1,-1,1),vec3(-1,hh,1),vec3(-1,hh,0),
               vec3(-1,hh,0),vec3(-1,-1,0),vec3(-1,-1,1)}
    local c = {cf,cf,cf,cf,cf,cf,cf,cf,cf,cf,cf,cf,
               cf,cf,cf,cf,cf,cf,cf,cf,cf,cf,cf,cf,
               cf,cf,cf,cf,cf,cf,cf,cf,cf,cf,cf,cf,
               cf,cf,cf,cf,cf,cf,cf,cf,cf,cf,cf,cf}
    for i,vv in pairs(v) do table.insert(verts,vv) end
    for i,cc in pairs(c) do table.insert(colors,cc) end
end

function createWalls(cm)
    walls = {}
    local height = #cm + 1
    for x = 2,#cm[1]-1 do
        for y = 2,#cm-1 do
            local char = string.sub(cm[height-y],x,x)
            local x1,x2 = (x-1)*physicsScale,x*physicsScale
            local y1,y2 = (y-1)*physicsScale,y*physicsScale
            local up = string.sub(cm[height-y-1],x,x) or nil
            local down = string.sub(cm[height-y+1],x,x) or nil
            local left = string.sub(cm[height-y],x-1,x-1) or nil
            local right = string.sub(cm[height-y],x+1,x+1) or nil
            if char == "X" then
                if up ~= "X" and up ~= "O" and up ~= nil then
                    table.insert(walls,physics.body(EDGE,vec2(x1,y2),vec2(x2,y2)))
                end
                if down ~= "X" and down ~= "O" and down ~= nil then
                    table.insert(walls,physics.body(EDGE,vec2(x1,y1),vec2(x2,y1)))
                end
                if left ~= "X" and left ~= "O" and left ~= nil then
                    table.insert(walls,physics.body(EDGE,vec2(x1,y1),vec2(x1,y2)))
                end
                if right ~= "X" and right ~= "O" and right ~= nil then
                    table.insert(walls,physics.body(EDGE,vec2(x2,y1),vec2(x2,y2)))
                end
            elseif char == "/" then
                table.insert(walls,physics.body(EDGE,vec2(x1,y1),vec2(x2,y2)))
                if up ~= "X" and up ~= "O" and up ~= nil and left == "X" then
                    table.insert(walls,physics.body(EDGE,vec2(x1,y2),vec2(x2,y2)))
                end
                if down ~= "X" and down ~= "O" and down ~= nil and right == "X" then
                    table.insert(walls,physics.body(EDGE,vec2(x1,y1),vec2(x2,y1)))
                end
                if left ~= "X" and left ~= "O" and left ~= nil and up == "X" then
                    table.insert(walls,physics.body(EDGE,vec2(x1,y1),vec2(x1,y2)))
                end
                if right ~= "X" and right ~= "O" and right ~= nil and down == "X" then
                    table.insert(walls,physics.body(EDGE,vec2(x2,y1),vec2(x2,y2)))
                end
            elseif char == "|" then
                table.insert(walls,physics.body(EDGE,vec2(x1,y2),vec2(x2,y1)))
                if up ~= "X" and up ~= "O" and up ~= nil and right == "X" then
                    table.insert(walls,physics.body(EDGE,vec2(x1,y2),vec2(x2,y2)))
                end
                if down ~= "X" and down ~= "O" and down ~= nil and left == "X" then
                    table.insert(walls,physics.body(EDGE,vec2(x1,y1),vec2(x2,y1)))
                end
                if left ~= "X" and left ~= "O" and left ~= nil and down == "X" then
                    table.insert(walls,physics.body(EDGE,vec2(x1,y1),vec2(x1,y2)))
                end
                if right ~= "X" and right ~= "O" and right ~= nil and up == "X" then
                    table.insert(walls,physics.body(EDGE,vec2(x2,y1),vec2(x2,y2)))
                end
            end
        end
    end
    for i,w in pairs(walls) do
        w.friction = 1
    end
end